#include <stdio.h>
#include <stdlib.h>
#include <lame/lame.h>
#include "util.h"
#include "audiosource.h"
#include "audioencoder_lame.h"

struct AUDIOENCODER_LAME {
	AUDIOENCODER encoder;

	FILE *file;
	AUDIOSOURCE *source;
	long framepos;
	int init_frames_written;
	lame_global_flags *lame;
};

/**
 * End of file?
 */
static int eof(AUDIOENCODER *e) {
	AUDIOENCODER_LAME *encoder=(AUDIOENCODER_LAME *)e;

	if (encoder->framepos>=audiosource_get_numsamples(encoder->source))
		return 1;

	else
		return 0;
}

/**
 * Encode.
 */ 
static int encode(AUDIOENCODER *e, int length) {
	TRACE("** lame encoder: encoding..\n");
	AUDIOENCODER_LAME *encoder=(AUDIOENCODER_LAME *)e;

	if (audioencoder_eof(e))
		return 0;

	int numframes=length*44100/1000;

	if (encoder->framepos+numframes>audiosource_get_numsamples(encoder->source))
		numframes=audiosource_get_numsamples(encoder->source)-encoder->framepos;

	float samples[2*numframes*sizeof(float)];

	TRACE("geting samples: %d\n",numframes);
	int res=audiosource_get_samples(encoder->source,samples,numframes);
	TRACE("get samples res: %d\n",res);
	if (!res)
		memset_float(samples,0,numframes*2);

	short intsamples[numframes*2];
	int i;

	TRACE("looping of frames: %d\n",numframes);
	for (i=0; i<numframes*2; i++) {
		float sample=samples[i];

		if (sample>1.0)
			sample=1.0;

		if (sample<-1.0)
			sample=-1.0;

		intsamples[i]=sample*0x7fff;
	}

	int outbuffer_size=1.25*numframes+7200;
	unsigned char outbuffer[outbuffer_size];
	int bytes;

	TRACE("calling encode buffer\n");
	bytes=lame_encode_buffer_interleaved(encoder->lame,intsamples,numframes,outbuffer,outbuffer_size);
	TRACE("encode bytes: %d\n",bytes);
	fwrite(outbuffer,bytes,1,encoder->file);

	encoder->framepos+=numframes;

	if (audioencoder_eof(e)) {
		bytes=lame_encode_flush(encoder->lame,outbuffer,outbuffer_size);
		fwrite(outbuffer,bytes,1,encoder->file);
	}

	return numframes;
}

/**
 * Destructor.
 */
static void dispose(AUDIOENCODER *e) {
	AUDIOENCODER_LAME *encoder=(AUDIOENCODER_LAME *)e;

	if (encoder->file!=stdout)
		fclose(encoder->file);

	free(encoder);
}

/**
 * Constrcutor.
 */
AUDIOENCODER_LAME *audioencoder_lame_create(char *target, AUDIOSOURCE *source, int position) {
	AUDIOENCODER_LAME *encoder;

	encoder=nicemalloc(sizeof(AUDIOENCODER_LAME));

	encoder->encoder.encode_func=encode;
	encoder->encoder.eof_func=eof;
	encoder->encoder.dispose_func=dispose;

	if (target)
		encoder->file=fopen(target,"wb");

	else
		encoder->file=stdout;

	if (!encoder->file)
		fail("Unable to open outfile.");

	encoder->source=source;
	encoder->framepos=(long long)position*44100/1000;
	encoder->init_frames_written=0;

	encoder->lame=lame_init();
	if (!encoder->lame)
		fail("Failed to initialize lame");

	lame_set_num_channels(encoder->lame,2);
	lame_set_in_samplerate(encoder->lame,44100);
	lame_set_brate(encoder->lame,128);
	lame_set_mode(encoder->lame,1); // J-Stereo
	lame_set_quality(encoder->lame,5);   /* 2=high  5 = medium  7=low */ 

	int ret_code;
	ret_code=lame_init_params(encoder->lame);
	if (ret_code<0)
		fail("lame_init_params failed");

	TRACE("seting pos\n");
	audiosource_set_streampos(encoder->source,encoder->framepos);
	TRACE("pos set\n");

	return encoder;
}
